package com.nali.dal.mongo.explain.operate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.nali.dal.expression.ExpressionValue;
import com.nali.dal.expression.PropertyExpression;
import com.nali.dal.expression.operate.Modifier;
import com.nali.dal.expression.query.Criteria;
import com.nali.dal.mongo.explain.query.Condition;

public class Operate {
	private Map<Modifier, List<PropValue>> operates=new HashMap<Modifier, List<PropValue>>();
	private Set<String> operatedPops=new HashSet<String>();
	private int paramsLength=0;

	/**
	 * 添加查询条件
	 */
	public void add(Modifier modifier, String property) {
		add(modifier, new PropValue(property, paramsLength++));
	}
	/**
	 * 添加查询条件
	 */
	public void add(Modifier modifier, String property, Object value) {
		add(modifier, new PropValue(property, tranValue(modifier, value)));
	}
	private Object tranValue(Modifier modifier, Object value) {
		switch (modifier.type) {
		case normal:
			return value;
		case dualQuery:
			return toQueryDBObject(value);
		default:
			throw new IllegalArgumentException("not supported Modifier type:" + modifier.type);
		}
	}
	@SuppressWarnings("unchecked")
	private Object toQueryDBObject(Object value) {//TODO 目前不支持占位参数
		if (value instanceof List) {
			List<ExpressionValue<Criteria>> conds = (List<ExpressionValue<Criteria>>) value;
			Condition condition = new Condition();
			for (ExpressionValue<Criteria> criteriaValue : conds) {
				PropertyExpression<Criteria> pc = criteriaValue.getPropertyExpression();
				condition.add(pc.getExpression(), pc.getPropertyName(), criteriaValue.getValue());
			}
			return condition.getQueryDBObject(Collections.emptyList());
		} else {
			return value;
		}
	}
	private void add(Modifier modifier, PropValue operateDesc) {
		List<PropValue> propList = operates.get(modifier);
		if(propList==null) {
			propList = new ArrayList<PropValue>();
			operates.put(modifier, propList);
		}
		propList.add(operateDesc);
		if(!operatedPops.add(operateDesc.prop)) {
			throw new IllegalArgumentException("duplicate prop:" + operateDesc.prop);
		}
	}
	/**
	 * 获取操作参数
	 */
	public DBObject getOperateDBObject(List<Object> params, int offset) {
		return getOperateDBObject(params.subList(offset, params.size()));
	}
	/**
	 * 获取操作参数
	 */
	public DBObject getOperateDBObject(List<Object> params) {
		BasicDBObject bson = getDBObject(operates.size());
		for (Entry<Modifier, List<PropValue>> entry: operates.entrySet()) {
			Modifier modifier = entry.getKey();
			List<PropValue> pvs = entry.getValue();
			bson.append(tranModifier(modifier), tranPvs(pvs, params));
		}
		return bson;
	}
	private BasicDBObject tranPvs(List<PropValue> pvs, List<Object> params) {
		BasicDBObject bson = getDBObject(pvs.size());
		for (PropValue pv : pvs) {
			bson.append(pv.prop, pv.getParam(params));
		}
		return bson;
	}
	private String tranModifier(Modifier modifier) {
		return "$"+modifier;
	}
	private static BasicDBObject getDBObject(int size) {
		return new BasicDBObject((int) Math.ceil(size / 0.75f));
	}
}

class PropValue {
	String prop;
	private int idx;
	private Object value;
	PropValue(String prop, int idx) {
		this.prop = prop;
		this.idx = idx;
	}
	public PropValue(String prop, Object value) {
		this.prop = prop;
		this.value = value;
		idx = -1;
	}
	public Object getParam(List<Object> params) {
		if(idx != -1) {
			if(idx>=params.size()) {
				throw new IllegalArgumentException("params' size is too small, at least:" + idx);
			}
			return params.get(idx);
		} else {
			return value;
		}
	}
}